/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package station3d;

import java.awt.*;
import java.awt.event.*;
import javax.media.opengl.*;

import station3d.graphics.*;

import static station3d.GameStateFactory.GameStateID;

/**
 *
 * @author Martin
 */
public class Station3D {

    private static final int DEFAULT_WIDTH = 800;
    private static final int DEFAULT_HEIGHT = 600;

    private static final GameStateID FIRST_STATE_ID = GameStateID.SIMULATION;


    private final Frame awtFrame;
    private final GLCanvas glCanvas;

    private volatile boolean running = false;
    private volatile boolean changeState = false;
    private volatile GameState currentState;
    private volatile GameState nextState = null;

    private final Renderer renderer;
    private final InputManager inputManager;
    private final ResourceManager resourceManager;


    private volatile int frames = 0;


    public Station3D() {
        awtFrame = new Frame("Station3D");

        // provizorní zavírání okna
        awtFrame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                // running = false;
                changeState = true;
                nextState = null;
            }
        });


        glCanvas = new GLCanvas();
        glCanvas.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
        glCanvas.addGLEventListener(new St3DGLListener());

        awtFrame.setResizable(false);
        awtFrame.setBackground(Color.BLACK);

        awtFrame.add(glCanvas);

        // vytvořit renderer, správce vstupů, správce GUI...
        renderer = new Renderer();

        inputManager = new InputManager();
        glCanvas.addMouseListener(inputManager);
        glCanvas.addMouseMotionListener(inputManager);
        glCanvas.addMouseWheelListener(inputManager);
        glCanvas.setFocusable(false);
        awtFrame.addKeyListener(inputManager);

        resourceManager = new ResourceManager();
        
    }


    /** Spustí hru. */
    public void run() {

        // vytvořit herní stav a inicializovat ho
        currentState = GameStateFactory.createGameState(FIRST_STATE_ID);
        currentState.setInputManager(inputManager);
        currentState.setResourceManager(resourceManager);

        // nastavit na fullscreen
        GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();

        boolean fullscreen = device.isFullScreenSupported();
        //fullscreen = false;
        if (fullscreen) {
            DisplayMode mode = new DisplayMode(device.getDisplayMode().getWidth(), device.getDisplayMode().getHeight(), 32, DisplayMode.REFRESH_RATE_UNKNOWN);
            awtFrame.setUndecorated(true);
            awtFrame.setVisible(true);
            device.setFullScreenWindow(awtFrame);
            device.setDisplayMode(mode);
        }
        else {
            awtFrame.pack();
            awtFrame.setVisible(true);
        }

        // při zavolání init() z GL listeneru se automaticky načte první herní stav


        // hlavní smyčka
        long startTime = System.currentTimeMillis()*1000;
        long currentTime = startTime;

        SmoothTimer smoothTimer = new SmoothTimer(100);

        long totalSmoothTime = 0;

        running = true;
        while (running) {
            // vykreslování - volá metodu display() z posluchače JOGL událostí
            glCanvas.display();

            long deltaTime = System.currentTimeMillis()*1000 - currentTime;
            currentTime += deltaTime;

            synchronized (inputManager) {
                // aktualizace stavu
                deltaTime = smoothTimer.getDeltaTime(deltaTime);
                totalSmoothTime += deltaTime;
                currentState.update(deltaTime);
            }
            

            // vyměnit stav, je-li třeba
            if (currentState.isFinished()) {
                /*running = false;
                GameStateID nextStateID = currentState.getNextStateID();
                currentState.shutdown();
                System.gc(); // zavolat garbage collector pro uvolnění paměti

                if (nextStateID != null) {
                    currentState = GameStateFactory.createGameState(nextStateID);
                    currentState.setInputManager(inputManager);

                    loadResources = true;
                    glCanvas.display();

                    // výměna stavu mohla nějakou dobu trvat, tak ji přeskočíme
                    deltaTime = System.currentTimeMillis() - currentTime;
                    currentTime += deltaTime;

                    smoothTimer.reset();

                    running = true;
                }*/
                GameStateID nextStateID = currentState.getNextStateID();

                if (nextStateID != null) {
                    nextState = GameStateFactory.createGameState(nextStateID);
                    nextState.setInputManager(inputManager);
                    nextState.setResourceManager(resourceManager);
                }

                changeState = true;
                glCanvas.display();

                deltaTime = System.currentTimeMillis() - currentTime;
                currentTime += deltaTime;

                smoothTimer.reset();
            }
        }

        long totalFpsTime = System.currentTimeMillis()*1000 - startTime;

        String msg = String.format("Total time: %d\nFrames: %d\nFPS: %f\nTotal smooth time: %d\n", totalFpsTime, frames, frames / (totalFpsTime / 1000000f), totalSmoothTime);
        System.out.printf(msg);
        


        // zavřít okno
        awtFrame.dispose();
    }
    
    
    


    /** Posluchač JOGL událostí. */
    private class St3DGLListener implements GLEventListener {

        @Override
        public void init(GLAutoDrawable drawable) {
            GL gl = drawable.getGL();

            System.out.println(gl.glGetString(GL.GL_VERSION));

            renderer.setGL(gl);
            renderer.init();

            try {
                currentState.init();
            }
            catch (ResourceError e) {
                e.printStackTrace();
                System.exit(1);
            }
        }

        @Override
        public void display(GLAutoDrawable drawable) {
            //System.out.println("displej");

            GL gl = drawable.getGL();
            renderer.setGL(gl);

            renderer.startFrame();
            currentState.draw(renderer);
            renderer.endFrame();

            frames++;

            // na konci snímku případně vyměnit stav
            if (changeState) {
                currentState.shutdown();
                System.gc();
                if (nextState != null) {
                    try {
                        nextState.init();
                    }
                    catch (ResourceError e) {
                        e.printStackTrace();
                        System.exit(1);
                    }
                    currentState = nextState;
                    nextState = null;
                    changeState = false;
                }
                else {
                    running = false;
                }
            }
        }

        @Override
        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
            // aktualizovat informace pro renderer a input manager
            renderer.setNewWidth(width);
            renderer.setNewHeight(height);

            inputManager.setViewportWidth(width);
            inputManager.setViewportHeight(height);

            // nastavit nový OpenGL viewport
            GL gl = drawable.getGL();
            gl.glViewport(0, 0, width, height);
        }

        @Override
        public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
            throw new UnsupportedOperationException("Not supported yet.");
        }

    }


    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Station3D().run();
    }

}
